﻿using System.Collections.Generic;
using System.Linq;
using System.Threading;
using ETModel;

namespace ETHotfix {
    /// <summary>
    /// Gate玩家等待断线重连组件,需要附加给全局,热更框架会自动附加
    /// </summary>
    public class GatePlayerWaitReconnectComponent : Component {
        /// <summary>
        /// 等待重新连接的玩家,但超时会被清理,insId=>cancel
        /// </summary>
        private readonly Dictionary<long, CancellationTokenSource> waitReconnectPlayers = new Dictionary<long, CancellationTokenSource>();

        /// <summary>
        /// 等待重连的时长(默认10秒)
        /// </summary>
        public long WaitReconnectTime = 10000;


        public override void Dispose() {
            base.Dispose();
        }

        /// <summary>
        /// gatePlayer玩家的连接断开了(一般以GateClientSession要销毁作为判断,为组件内部自行调用)
        /// </summary>
        /// <param name="disconnectPlayer"></param>
        public void GatePlayerDisconnect(GatePlayer disconnectPlayer) {
            var insId = disconnectPlayer.InstanceId;
            CancellationTokenSource cancel;
            if (waitReconnectPlayers.TryGetValue(insId, out cancel)) {
                cancel.Cancel();
            }
            cancel = new CancellationTokenSource();
            waitReconnectPlayers[insId] = cancel;
            StartWaitDisconnect(disconnectPlayer, cancel.Token).Coroutine();
        }
        private async ETVoid StartWaitDisconnect(GatePlayer disconnectPlayer, CancellationToken cancelToken) {
            var insId = disconnectPlayer.InstanceId;
            await Game.Scene.GetComponent<TimerComponent>().WaitAsync(WaitReconnectTime, cancelToken);
            if (cancelToken.IsCancellationRequested) {
                //如果是取消才完成等待的,则跳过
                return;
            }
            if (insId != disconnectPlayer.InstanceId || disconnectPlayer.IsDisposed) {
                //如果实例已经被回收过了,则也不用处理
                return;
            }

            //玩家断开的做法是直接把这个Player销毁,各自订阅的地方自行做处理
            disconnectPlayer.Dispose();
        }

        /// <summary>
        /// gatePlayer玩家重新连接上了(调用gateSession.GatePlayerReconnect(...)的话,本方法会自动被调用)
        /// </summary>
        /// <param name="reconnectPlayer"></param>
        public void GatePlayerReconnect(GatePlayer reconnectPlayer) {
            var insId = reconnectPlayer.InstanceId;
            CancellationTokenSource cancel;
            if (waitReconnectPlayers.TryGetValue(insId, out cancel)) {
                cancel.Cancel();
                waitReconnectPlayers.Remove(insId);
            }
        }
        /// <summary>
        /// gatePlayer如果已经被调用销毁了,就不需要等待,直接清理
        /// </summary>
        /// <param name="reconnectPlayer"></param>
        public void GatePlayerDestroy(GatePlayer player) {
            var insId = player.InstanceId;
            CancellationTokenSource cancel;
            if (waitReconnectPlayers.TryGetValue(insId, out cancel)) {
                cancel.Cancel();
                waitReconnectPlayers.Remove(insId);
            }
        }
    }

    /// <summary>
    /// 
    /// </summary>
    [ObjectSystem]
    public class GatePlayerDestroySystem : DestroySystem<GatePlayer> {
        public override void Destroy(GatePlayer self) {

            //不需要等待断线重连了
            Game.Scene.GetComponent<GatePlayerWaitReconnectComponent>().GatePlayerDestroy(self);

            //通知map服务器
            if (self.MapPlayerInstanceId > 0) {
                //如果有连到Map服务器了,则给Map服务器发送断线消息
                var actorMsgSender = Game.Scene.GetComponent<ActorMessageSenderComponent>().Get(self.MapPlayerInstanceId);
                actorMsgSender.Call(new G2M_SessionDisconnect()).Coroutine();
            }
        }
    }
}
